#include "CefViewWidget.h"
#include <core/QbLogger.h>
#if defined(Q_OS_WIN)
#include <algorithm>
#elif defined(Q_OS_MAC)
#elif defined(Q_OS_LINUX)
#else
#endif
#include <QUrl>
#include <QUrlQuery>
#include "DownloadManager.h"
#include "BrowserDialog.h"
#include <QVBoxLayout>
#include <QMetaMethod>
#include <QStyle>

const char kTagTab[] = "tab";


CefViewWidget::CefViewWidget(const QString& url, const QCefSetting* setting, QWidget* parent /* = 0*/)
    : QCefView(url, setting, parent), start_url(url)
{
    log_warning("CefViewWidget ctor hit");
    QObject::connect(this, &CefViewWidget::cefQueryRequest, this, &CefViewWidget::onCefQueryRequest);
}

CefViewWidget::~CefViewWidget() {
    log_warning("CefViewWidget dtor hit");
}

void CefViewWidget::setDelegate(QSharedPointer<CefViewDelegate> p)
{
    delegate_ = p;
    if (delegate_) {
        delegate_->addCefViewWidget(start_url, this);
    }
}

BrowserDialog *CefViewWidget::createPopup(const QString &url, QSharedPointer<CefViewDelegate> delegate)
{
    BrowserDialog *dlg = new BrowserDialog();
    CefViewWidget* popup = new CefViewWidget(url, nullptr, dlg);

    popup->setDelegate(delegate);
    connect(popup, &CefViewWidget::titleChanged, dlg, &BrowserDialog::setTitle);
    dlg->setContentWidget(popup);

    dlg->setAttribute(Qt::WA_DeleteOnClose, true);
    return dlg;
}

QCefView *CefViewWidget::onNewBrowser(qint64 sourceFrameId,
                                      const QString &url,
                                      const QString &name,
                                      CefWindowOpenDisposition targetDisposition,
                                      QRect &/*rect*/,
                                      QCefSetting &/*settings*/)
{
    //const QJsonObject popupFeatures;
    //return handleNewPopup(sourceFrameId, url, name, targetDisposition, popupFeatures);
    QUrl temp(url);
    QUrlQuery query(temp);
    bool open_in_popup = false;
    if ( query.hasQueryItem(kTagTab) ) {
        open_in_popup = (query.queryItemValue(kTagTab) == "false");
        query.removeQueryItem(kTagTab);
        temp.setQuery(query);
    }

    if (open_in_popup) {
        const QJsonObject popupFeatures;
        handleNewPopup(sourceFrameId, temp.toString(), name, targetDisposition, popupFeatures);
        return nullptr;
    }

    if (delegate_) {
        delegate_->onNewBrowser(url);
    }

    return nullptr; // the return value does not make sense to QCefView
}

bool
CefViewWidget::onNewPopup(qint64 sourceFrameId,
                          const QString& targetUrl,
                          QString& targetFrameName,
                          QCefView::CefWindowOpenDisposition targetDisposition,
                          const QJsonObject& popupFeatures,
                          QRect& /*rect*/,
                          QCefSetting& /*settings*/,
                          bool& /*disableJavascriptAccess*/)
{
    log_warning("onNewPopup for url: %s", targetUrl.toStdString().c_str());

    return nullptr != handleNewPopup(sourceFrameId, targetUrl, targetFrameName, targetDisposition, popupFeatures); //expect to be true: cancel popup
}

QCefView *
CefViewWidget::handleNewPopup(qint64 sourceFrameId, const QString &url, const QString &name, CefWindowOpenDisposition targetDisposition, const QJsonObject &popupFeatures)
{
    if (delegate_) {
        if (CefViewWidget* popup = delegate_->getCefViewWidget(url)) {
            log_warning("NewPopup canceld for existing url");
            if (QWidget* container = popup->parentWidget()) {
                container->raise();
                container->show();
            }
            return popup;
        }
    }

    BrowserDialog *dlg = new BrowserDialog();
    CefViewWidget* popup = new CefViewWidget(url, nullptr, dlg);

    if (delegate_) {
        popup->setDelegate(delegate_);
    }

    connect(popup, &CefViewWidget::titleChanged, dlg, &BrowserDialog::setTitle);
    /*if (!name.isEmpty()) {
        dlg->setWindowTitle(name);
    }*/

    dlg->setContentWidget(popup);

    {
        QScreen * sc = this->screen();
        int width = popupFeatures.contains("width") ? popupFeatures.value("width").toInt() : (sc->size().width() - 16 * 20);
        int height = popupFeatures.contains("height") ? popupFeatures.value("height").toInt() : (sc->size().height() - 9 * 20);

        width = std::min(width, sc->size().width() - 16 * 4);
        height = std::min(height, sc->size().height() - 9 * 4);

        int x = popupFeatures.contains("x") ? popupFeatures.value("x").toInt() : /*middle of horizen*/((sc->size().width() - width)/2);
        int y = popupFeatures.contains("y") ? popupFeatures.value("y").toInt() : /*middle of vertical*/((sc->size().height() - height)/2);

        x += sc->geometry().x();
        y += sc->geometry().y();

        x = std::max(x, sc->geometry().x());
        x = std::min(x, sc->geometry().right() - width);
        y = std::max(y, sc->geometry().y());
        y = std::min(y, sc->geometry().bottom() - height);

        dlg->setGeometry(x, y, width, height);
    }

    dlg->setAttribute(Qt::WA_DeleteOnClose, true);
    dlg->setAttribute(Qt::WA_ShowModal, this->topLevelWidget()->isModal());
    //this->topLevelWidget()->stackUnder(dlg);

    dlg->show();

    return popup;
}

void
CefViewWidget::onNewDownloadItem(const QSharedPointer<QCefDownloadItem>& item, const QString& suggestedName)
{
  DownloadManager::getInstance().AddNewDownloadItem(item);
}

void
CefViewWidget::onUpdateDownloadItem(const QSharedPointer<QCefDownloadItem>& item)
{
  DownloadManager::getInstance().UpdateDownloadItem(item);
}

void CefViewWidget::onCefQueryRequest(int browserId, qint64 frameId, const QCefQuery &query)
{
    if (delegate_) {
        delegate_->onCefQuery(browserId, frameId, query);
    }
}

bool CefViewDelegate::executeJavascript(int browserId, qint64 frameId, const QString &code, const QString &url)
{
    CefViewWidget* ptr_ = getCefViewWidget(browserId);
    if (ptr_) {
        return ptr_->executeJavascript(frameId, code, url);
    }
    return false;
}

CefViewWidget* CefViewDelegate::getCefViewWidget(const QString &url) const
{
    auto iter = start_url2View.find(url);
    return iter != start_url2View.end() ? iter->second.data() : nullptr;
}

void CefViewDelegate::addCefViewWidget(const QString &url, CefViewWidget* p)
{
    // remove empty pointers
    for(auto iter = start_url2View.begin(); iter != start_url2View.end(); ) {
        if (!(iter->second)) {
            iter = start_url2View.erase(iter);
        } else {
            ++iter;
        }
    }

    start_url2View[url] = QPointer<CefViewWidget>(p);
}

CefViewWidget *CefViewDelegate::getCefViewWidget(const int &browserId) const
{
    auto iter = std::find_if(start_url2View.begin(), start_url2View.end(), [browserId](const std::map<const QString, QPointer<CefViewWidget>>::value_type& pair)->bool{
        if (pair.second)
            return pair.second->browserId() == browserId;
        return false;
    });

    if (iter != start_url2View.end()) {
        return iter->second.data();
    }

    return nullptr;
}
